#include <stdlib.h>
#include "transContentMemory.hpp"

const int TransContentMemory::BUFFER_FREE = 0;
const int TransContentMemory::BUFFER_BUSY = 1;

TransContentMemory* TransContentMemory::_instance = NULL;

TransContentMemory::TransContentMemory(){
	debug_enter();
	pthread_mutex_init(&mMutexBuffer, NULL);
	mRecordCount = TransContentSetting::getMemoryBufferCount();
	mRecordSize = TransContentSetting::getMemoryBufferSize();
	mUsedRecordCount = 0;
	bufferBase = NULL;
	bufferStateArray = NULL;
	mState = INS_STATE_ERROR;
	if(allocMemroy() != E_TRANC_SUCESS){
		debug_leave();
		return;
	}
	mState = INS_STATE_OK;
	debug_leave();
}

TransContentMemory::~TransContentMemory(){
	debug_enter();
	mState = INS_STATE_ERROR;
	pthread_mutex_destroy(&mMutexBuffer);
	freeMemory();
	debug_leave();
}

int TransContentMemory::allocMemroy(){
	bufferBase = (char*)calloc(mRecordCount, mRecordSize);
	if(bufferBase == NULL){
		debug_record(logger::LEVEL_ERROR, "alloc memory for client failed: errno=%d==>%s", errno, strerror(errno));		
		return E_TRANC_SystemError;
	}
	bufferStateArray = (char*)calloc(mRecordCount, 1);
	if(bufferStateArray == NULL){
		debug_record(logger::LEVEL_ERROR, "alloc memroy for client failed: error=%d==>%s", errno, strerror(errno));
		return E_TRANC_SystemError;
	}
	return E_TRANC_SUCESS;
}

int TransContentMemory::freeMemory(){
	debug_enter();
	if(bufferBase != NULL){
		debug_record(logger::LEVEL_WARMMING, "free base buffer address for client");
		free(bufferBase);
		bufferBase = NULL;
	}
	if(bufferStateArray != NULL){
		debug_record(logger::LEVEL_WARMMING, "free state buffer address for client");
		free(bufferStateArray);
		bufferStateArray = NULL;
	}
	debug_leave();
	return E_TRANC_SUCESS;
}

TransContentMemory* TransContentMemory::getInstance(){
	debug_enter();
	if(_instance == NULL){
		debug_record(logger::LEVEL_INFO, "create new instance of TransContentMemory");
		_instance = new TransContentMemory();
		if( _instance->getState() != INS_STATE_OK){
			debug_record(logger::LEVEL_ERROR, "create instance of TransContentMemory failed");
			releaseInstance();
		}
	}
	debug_leave();
	return _instance;
}

int TransContentMemory::getState(){
	return mState;
}

void TransContentMemory::releaseInstance(){
	debug_enter();
	if(_instance != NULL){
		debug_record(logger::LEVEL_WARMMING, "release TransContentMemory");
		delete _instance;
		_instance = NULL;
	}
	debug_leave();
}

int TransContentMemory::getRecordCount(){
	return mRecordCount;
}

int TransContentMemory::getRecordSize(){
	return mRecordSize;
}

int TransContentMemory::getUsedRecordCount(){
	return mUsedRecordCount;
}

int TransContentMemory::getFreeRecordCount(){
	return mRecordCount - mUsedRecordCount;
}

/**
 * get a buffer from buffer stack
 * 
 * Notice: 
 *  wherever wnat to get a buffer to store record, will call this method,
 *  after finished use the buffer call releaseBuffer
 *  @see relaseBuffer
 */      
int TransContentMemory::getBuffer(char** record){
	int tempId = 0;
	debug_enter();
	pthread_mutex_lock(&mMutexBuffer);

	while(tempId < mRecordCount){
		if(bufferStateArray[tempId] == BUFFER_FREE){
			goto Sucess;
		}
		++tempId;
	}

	//increase buffer space
	if(increaseBuffer() == 0){
		//increase buffer sucess
		goto Sucess;
	}else{
		//increase buffer failed
		goto Error;
	}

Sucess:
	bufferStateArray[tempId] = BUFFER_BUSY;
	++mUsedRecordCount;
	*record = bufferBase + tempId * mRecordSize;
	log_info("get buffer sucess!!");

	goto End;
Error:
	tempId = E_TRANC_BUFFER_FULL;
	logd_error("get buffer failed!!");
End:
	pthread_mutex_unlock(&mMutexBuffer);
	debug_leave();
	return tempId;
}

//////////////////////////////////////////
/// increase the current buffer count to towice space
/// return 0 when sucess, others failed(-1)
//////////////////////////////////////////
int TransContentMemory::increaseBuffer(){
	if(mRecordCount >= MEMORY_RECORD_MAX_COUNT){
		logd_error("the buffer record count reached the max limit");
		return -1;
	}
	mRecordCount *= 2;
	int newSize = mRecordCount;
	bufferStateArray = (char*)realloc(bufferStateArray, newSize);
	if(bufferStateArray == NULL){
		mState = INS_STATE_ERROR;
		logd_fata_error("alloc memory for buffer failed!! :%s", strerror(errno));
		return -1;
	}

	newSize = mRecordCount * mRecordSize;
	bufferBase = (char*)realloc(bufferBase, newSize);
	if(bufferBase == NULL){
		logd_fata_error("alloc memroy for buffer failed!! :%s", strerror(errno));	
		return -1;
	}	
	return 0;
}

int TransContentMemory::releaseBuffer(int recordIndex){
	debug_enter();
	debug_record(logger::LEVEL_INFO, "release the buffer : index=%d", recordIndex);
	pthread_mutex_lock(&mMutexBuffer);
	--mUsedRecordCount;
	bufferStateArray[recordIndex] = BUFFER_FREE;
	pthread_mutex_unlock(&mMutexBuffer);
	debug_leave();
	return E_TRANC_SUCESS;
}

char* TransContentMemory::indexToBuffer(int index){
	return bufferBase + index * mRecordSize;
}
